#include "monitor/Expression.h"

static Rule rules[] = {
    { std::regex(R"(\s+)"),                  TokenType::SPACE },    // space
    { std::regex(R"(0[xX][0-9a-fA-F]+)"),    TokenType::NUM   },    // hex num
    { std::regex(R"([0-9]+)"),               TokenType::NUM   },    // dec num
    { std::regex(R"(\$[0-9a-zA-Z]+)"),       TokenType::REG   },    // reg
    
    { std::regex(R"(\+)"),                   TokenType::ADD   },    // plus
    { std::regex(R"(-)"),                    TokenType::SUB   },    // minus
    { std::regex(R"(\*)"),                   TokenType::MUL   },    // multiply
    { std::regex(R"(/)"),                    TokenType::DIV   },    // divide

    { std::regex(R"(==)"),                   TokenType::EQ    },    // equal
    { std::regex(R"(!=)"),                   TokenType::NEQ   },    // not equal
    { std::regex(R"(>)"),                    TokenType::GT    },    // greater than
    { std::regex(R"(<)"),                    TokenType::LT    },    // less than

    { std::regex(R"(&&)"),                   TokenType::AND   },    // and
    { std::regex(R"(\|\|)"),                 TokenType::OR    },    // or
    { std::regex(R"(!)"),                    TokenType::NOT   },    // not

    { std::regex(R"(\()"),                   TokenType::LPAR  },    // left parenthesis
    { std::regex(R"(\))"),                   TokenType::RPAR  },    // right parenthesis
};

#define NR_REGEXP ((sizeof rules)/(sizeof rules[0]))

bool Expression::make_token(const char* expr) {
    tokens.clear();
    std::cmatch match;
    int index = 0;
    while (expr[index] != '\0') {
        int i = 0;
        for (i = 0; i < NR_REGEXP; i++) {
            if (std::regex_search(expr + index, match, rules[i].regex)) {
                if (match.position() != 0) continue;
                index += match[0].length();
                if (rules[i].type == TokenType::SPACE) break;
                tokens.push_back({ rules[i].type, match[0] });
                break;
            }
        }
        if (i == NR_REGEXP) {
            std::printf("no match at index %d\n%s\n%*.s^\n", index, expr, index, "");
            return false;
        }
    }
    return true;
}

std::tuple<bool, word_t> Expression::parser(const char* expr) {
    if (!make_token(expr)) return { false, 0 };
    cur_idx = 0;
    bool success = true;
    word_t result = parse_expr(success);
    return { success, result };
}

// expr       := logic_or
word_t Expression::parse_expr(bool& success) {
    return parse_logic_or(success);
}

// logic_or   := logic_and ("||" and) *
word_t Expression::parse_logic_or(bool& success) {
    word_t result = parse_logic_and(success);
    if (!success) return 0;
    while (cur_idx < tokens.size()) {
        if (tokens[cur_idx].type != TokenType::OR) break;
        cur_idx ++;
        word_t right = parse_logic_and(success);
        if (!success) return 0;
        result = result || right;
    }

    return result;
}

// logic_and  := eq_neq ("&&" eq_neq) *
word_t Expression::parse_logic_and(bool& success) {
    word_t result = parse_eq_neq(success);
    if (!success) return 0;
    while (cur_idx < tokens.size()) {
        if (tokens[cur_idx].type != TokenType::AND) break;
        cur_idx ++;
        word_t right = parse_eq_neq(success);
        if (!success) return 0;
        result = result && right;
    }

    return result;
}

// eq_neq     := gt_lt ("==" gt_lt | "!=" gt_lt)*
word_t Expression::parse_eq_neq(bool& success) {
    word_t result = parse_gt_lt(success);
    if (!success) return 0;
    while (cur_idx < tokens.size()) {
        auto type = tokens[cur_idx].type;
        if (type != TokenType::EQ && type != TokenType::NEQ) break;
        cur_idx ++;
        word_t right = parse_gt_lt(success);
        if (!success) return 0;
        if      (type == TokenType::EQ)     result = result == right;
        else /* (type == TokenType::NEQ) */ result = result != right;
    }

    return result;
}

// gt_lt      := add_sub ("<" add_sub | ">" add_sub)*
word_t Expression::parse_gt_lt(bool& success) {
    word_t result = parse_add_sub(success);
    if (!success) return 0;
    while (cur_idx < tokens.size()) {
        auto type = tokens[cur_idx].type;
        if (type != TokenType::GT && type != TokenType::LT) break;
        cur_idx ++;
        word_t right = parse_add_sub(success);
        if (!success) return 0;
        if      (type == TokenType::GT)    result = result > right;
        else /* (type == TokenType::LT) */ result = result < right;
    }

    return result;
}

// add_sub    := mul_div ("+" mul_div | "-" mul_div)*
word_t Expression::parse_add_sub(bool& success) {
    word_t result = parse_mul_div(success);
    if (!success) return 0;
    while (cur_idx < tokens.size()) {
        auto type = tokens[cur_idx].type;
        if (type != TokenType::ADD && type != TokenType::SUB) break;
        cur_idx ++;
        word_t right = parse_mul_div(success);
        if (!success) return 0;
        if      (type == TokenType::ADD)    result = result + right;
        else /* (type == TokenType::SUB) */ result = result - right;
    }

    return result;
}

// mul_div    := unary ("*" unary | "/" unary)*
word_t Expression::parse_mul_div(bool& success) {
    word_t result = parse_unary(success);
    if (!success) return 0;
    while (cur_idx < tokens.size()) {
        auto type = tokens[cur_idx].type;
        if (type != TokenType::MUL && type != TokenType::DIV) break;
        cur_idx ++;
        word_t right = parse_unary(success);
        if (!success) return 0;
        if      (type == TokenType::MUL)    result = result * right;
        else /* (type == TokenType::DIV) */ result = result / right;
    }

    return result;
}

// unary      := ("+" | "-" | "!" | "*")? primary
word_t Expression::parse_unary(bool& success) {
    auto type = tokens[cur_idx].type;
    if (type == TokenType::NOT) {
        cur_idx++;
        return !parse_unary(success);
    } 
    if (type == TokenType::ADD) {
        cur_idx++;
        return parse_unary(success);
    } 
    if (type == TokenType::SUB) {
        cur_idx++;
        return -parse_unary(success);
    }
    // if (type == TokenType::MUL) {
    //     cur_idx++;
    //     return vaddr_read(parse_unary(success), sizeof(word_t));
    // } 

    return parse_primary(success);
}

// primary    := num | reg | "(" expr ")"
word_t Expression::parse_primary(bool& success) {
    if (cur_idx >= tokens.size()) {
        success = false;
        return 0;
    }

    auto type = tokens[cur_idx].type;
    if (type == TokenType::NUM) {
        return std::strtoul(tokens[cur_idx++].value.c_str(), NULL, 0);
    }
    if (type == TokenType::REG && get_reg_val.operator bool()) {
        return get_reg_val(tokens[cur_idx++].value, success);
    }
    if (type == TokenType::LPAR) {
        cur_idx ++; // 跳过左括号
        word_t result = parse_expr(success);
        if (!success) return 0;
        if (tokens[cur_idx].type != TokenType::RPAR) {
            success = false;
            return 0;
        }
        cur_idx ++; // 跳过右括号
        return result;
    }

    success = false;
    return 0;
}
